package com.ruicar.afs.cloud.basic.proceeds.debit.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruicar.afs.cloud.basic.common.entity.BasicBankCard;
import com.ruicar.afs.cloud.basic.common.entity.BasicExpenseRecord;
import com.ruicar.afs.cloud.basic.common.entity.BasicRepaymentPlan;
import com.ruicar.afs.cloud.basic.common.service.impl.BasicBankCardServiceImpl;
import com.ruicar.afs.cloud.basic.common.service.impl.BasicExpenseRecordServiceImpl;
import com.ruicar.afs.cloud.basic.common.service.impl.BasicRepaymentPlanServiceImpl;
import com.ruicar.afs.cloud.basic.proceeds.debit.condition.SingleDebitCondition;
import com.ruicar.afs.cloud.basic.proceeds.debit.entity.BasicDebitRecord;
import com.ruicar.afs.cloud.basic.proceeds.debit.entity.BasicDebitRecordHistory;
import com.ruicar.afs.cloud.basic.proceeds.debit.mapper.BasicDebitRecordMapper;
import com.ruicar.afs.cloud.basic.proceeds.debit.service.BasicDebitRecordService;
import com.ruicar.afs.cloud.basic.proceeds.debit.vo.SingleDebitReadyVo;
import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.common.core.util.IResponse;
import com.ruicar.afs.cloud.common.modules.contract.enums.*;
import com.ruicar.afs.cloud.common.util.EmptyUtils;
import com.ruicar.afs.cloud.manage.api.dto.PrepaymentDebitDealStatusDto;
import com.ruicar.afs.cloud.manage.api.dto.PrepaymentInfoDto;
import com.ruicar.afs.cloud.manage.api.feign.cust.CustChangeFeign;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author: Lee
 * @date 2020-05-30 04:35:07 
 * @description 
 */
@Service
@Slf4j
public class BasicDebitRecordServiceImpl extends ServiceImpl<BasicDebitRecordMapper, BasicDebitRecord> implements BasicDebitRecordService {

    @Autowired
    private BasicRepaymentPlanServiceImpl basicRepaymentPlanService;
    @Autowired
    private BasicExpenseRecordServiceImpl basicExpenseRecordService;
    @Autowired
    private BasicBankCardServiceImpl basicBankCardService;
    @Autowired
    private BasicDebitRecordHistoryServiceImpl basicDebitRecordHistoryService;
    @Autowired
    private BasicDebitRecordMapper basicDebitRecordMapper;
    @Autowired
    private CustChangeFeign custChangeFeign;

    @Override
    public boolean isAllowedChangeRecord(String contractNo) {
        List<BasicDebitRecord> debitRecordList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,contractNo)
                .eq(BasicDebitRecord::getDebitStatus, DebitStatusEnum.debiting)
                .gt(BasicDebitRecord::getGoingAmt,BigDecimal.ZERO)
        );

        return EmptyUtils.isEmpty(debitRecordList);
    }

    @Override
    public void addDebitRecordList(String contractNo, Date batchDate) {
        /**  产生新的扣款数据  **/
        List<BasicDebitRecord> basicDebitRecords = new ArrayList<>();
        /**  提前还款的数据处理 by ZC.GUO  **/
        List<String> prepaymentContractNos = new ArrayList<>();
        IResponse<List<PrepaymentInfoDto>> response = custChangeFeign.getPrepaymentRecordInfo();
        if (CommonConstants.SUCCESS.equals(response.getCode())){
            List<PrepaymentInfoDto> prepaymentInfoDtoList = response.getData();
            if (EmptyUtils.isNotEmpty(prepaymentInfoDtoList)){
                prepaymentContractNos = prepaymentInfoDtoList.stream().map(PrepaymentInfoDto::getContractNo).collect(Collectors.toList());
                basicDebitRecords = this.takePrepaymentRecord(contractNo,basicDebitRecords,prepaymentInfoDtoList,batchDate);
            }
        }
        /**  合同还款计划表basic_repayment_plan，可获取本金、利息  **/
        basicDebitRecords = this.takeRepaymentPlan(basicDebitRecords,contractNo,prepaymentContractNos,batchDate);
        /**  合同费用记录表basic_expense_record，罚息、提前结清款、催收费用、变更费用、提前还款手续费、其他  **/
        basicDebitRecords = this.takeExpenseRecord(basicDebitRecords,contractNo,prepaymentContractNos,batchDate);

        /**  数据归档  **/
        this.storeDebitRecord(contractNo);

        this.saveBatch(basicDebitRecords);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PrepaymentDebitDealStatusDto dealRecordForPrepaymentImmediately(PrepaymentInfoDto prepaymentInfoDto, Date batchDate) {
        PrepaymentDebitDealStatusDto dto = new PrepaymentDebitDealStatusDto();
        dto.setDealStatus("2");
        dto.setStatusDesc("处理成功");
        if (EmptyUtils.isNotEmpty(prepaymentInfoDto)){
            /**  约定扣款日不是当天的不进行处理 by ZC.GUO  **/
            if (DateUtil.isSameDay(batchDate,prepaymentInfoDto.getRepayDate())){
                /**  只对代扣的提前还款进行处理 by ZC.GUO  **/
                if (ReceiptSourceTypeEnum.bankDeduct.equals(prepaymentInfoDto.getRepayMode())){
                    List<BasicDebitRecord> debitRecordList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                            .eq(BasicDebitRecord::getContractNo,prepaymentInfoDto.getContractNo())
                            .isNotNull(BasicDebitRecord::getBatchId)
                            .ne(BasicDebitRecord::getFeeType, FeeTypeEnum.advanceSettle)
                    );
                    /**  有该合同正在扣款中的扣款指令 by ZC.GUO  **/
                    if (EmptyUtils.isNotEmpty(debitRecordList)){
                        /**  插入不可扣款的提前结清扣款指令，等待正在扣款的指令有结果后进行处理 by ZC.GUO  **/
                        this.addSingleDebitRecordForPrepayment(prepaymentInfoDto,DebitStatusEnum.notMayDebit, batchDate);
                        /**  返回结果 by ZC.GUO  **/
                        dto.setDealStatus("2");
                        dto.setStatusDesc("处理成功");
                    }
                    /**  没有该合同正在扣款中的扣款指令 by ZC.GUO  **/
                    else{
                        debitRecordList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                                .eq(BasicDebitRecord::getContractNo,prepaymentInfoDto.getContractNo())
                                .gt(BasicDebitRecord::getSucceedAmt,BigDecimal.ZERO)
                        );
                        /**  该合同的当日扣款指令已经扣款成功（部分或全部） by ZC.GUO  **/
                        if (EmptyUtils.isNotEmpty(debitRecordList)){
                            /**  处理扣款指令（所有指令归档，未扣款成功金额初始化） by ZC.GUO  **/
                            this.storeDebitRecord(prepaymentInfoDto.getContractNo(),true);
                            /**  返回结果 by ZC.GUO  **/
                            dto.setDealStatus("3");
                            dto.setStatusDesc("有扣款成功记录，需重新报价");
                            /**  提前结清报价失效 by ZC.GUO  **/
                            custChangeFeign.prepaymentRecordInvalid(prepaymentInfoDto.getContractNo());
                        }
                        else{
                            /**  处理现有指令（所有指令归档） by ZC.GUO  **/
                            this.storeDebitRecord(prepaymentInfoDto.getContractNo(),false);
                            /**  插入新的可扣款的提前结清扣款指令 by ZC.GUO  **/
                            this.addSingleDebitRecordForPrepayment(prepaymentInfoDto,DebitStatusEnum.mayDebit,batchDate);
                            /**  返回结果 by ZC.GUO  **/
                            dto.setDealStatus("2");
                            dto.setStatusDesc("处理成功");
                        }
                    }
                }
            }
        }

        return dto;
    }

    /**
     * 对未扣款成功金额进行初始化
     * @author ZC.GUO
     * @param
     * @return
     */
    public BasicDebitRecord initForUnSuccessAmt(BasicDebitRecord record){
        BasicDebitRecord debitRecord = new BasicDebitRecord();
        if (EmptyUtils.isNotEmpty(record)){
            BeanUtils.copyProperties(record,debitRecord);
            debitRecord.setId(null);
            debitRecord.setReadyAmt(record.getReadyAmt().subtract(record.getSucceedAmt()));
            debitRecord.setSucceedAmt(BigDecimal.ZERO);
            debitRecord.setGoingAmt(BigDecimal.ZERO);
            debitRecord.setDebitStatus(DebitStatusEnum.mayDebit);
            debitRecord.setBatchId(null);
            debitRecord.setWriteOffAmt(BigDecimal.ZERO);
            debitRecord.setManualNum(BigDecimal.ZERO.intValue());
            debitRecord.setRevision(BigDecimal.ZERO.longValue());
        }
        return debitRecord;
    }

    /**
     *
     * <p>Description: 每日代扣数据归档</p>
     *
     * @param contractNo 合同编号
     * @param type 是否需要对未扣款成功金额进行初始化
     * @return
     */
    public void storeDebitRecord(String contractNo,Boolean type){

        List<BasicDebitRecordHistory> debitRecordHistoryList = new ArrayList<>();
        List<BasicDebitRecord> debitRecordList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,contractNo)
        );
        if (EmptyUtils.isNotEmpty(debitRecordList)){
            /**  需要对未扣款成功金额进行初始化 by ZC.GUO  **/
            if (type){
                List<BasicDebitRecord> newDebitRecordList = new ArrayList<>();
                for (BasicDebitRecord oldDebitRecord:debitRecordList){
                    BasicDebitRecord newDebitRecord = this.initForUnSuccessAmt(oldDebitRecord);
                    if (EmptyUtils.isNotEmpty(newDebitRecord.getReadyAmt())&&newDebitRecord.getReadyAmt().compareTo(BigDecimal.ZERO)>0){
                        newDebitRecordList.add(newDebitRecord);
                    }
                    BasicDebitRecordHistory debitRecordHistory = new BasicDebitRecordHistory();
                    BeanUtils.copyProperties(oldDebitRecord,debitRecordHistory);
                    debitRecordHistory.setId(null);
                    debitRecordHistory.setStoreDate(Calendar.getInstance().getTime());
                    debitRecordHistoryList.add(debitRecordHistory);
                    /**  删除本条数据  **/
                    this.removeById(oldDebitRecord.getId());
                }
                /**  初始化保存 by ZC.GUO  **/
                if (EmptyUtils.isNotEmpty(newDebitRecordList)){
                    this.saveBatch(newDebitRecordList);
                }
            }
            /**  不需要对未扣款成功金额进行初始化 by ZC.GUO  **/
            else{
                for (BasicDebitRecord oldDebitRecord:debitRecordList){
                    BasicDebitRecordHistory debitRecordHistory = new BasicDebitRecordHistory();
                    BeanUtils.copyProperties(oldDebitRecord,debitRecordHistory);
                    debitRecordHistory.setId(null);
                    debitRecordHistory.setStoreDate(Calendar.getInstance().getTime());
                    debitRecordHistoryList.add(debitRecordHistory);
                    /**  删除本条数据  **/
                    this.removeById(oldDebitRecord.getId());
                }
            }
            /**  历史数据保存  **/
            if(EmptyUtils.isNotEmpty(debitRecordHistoryList)){
                basicDebitRecordHistoryService.saveBatch(debitRecordHistoryList);
            }
        }
    }
    @Override
    public void addSingleDebitRecordForPrepayment(PrepaymentInfoDto prepaymentInfoDto, DebitStatusEnum status, Date batchDate){

        if (EmptyUtils.isNotEmpty(prepaymentInfoDto)){
            BasicDebitRecord debitRecord = new BasicDebitRecord();
            debitRecord.setContractNo(prepaymentInfoDto.getContractNo());
            debitRecord.setFeeType(FeeTypeEnum.advanceSettle);
            debitRecord.setSourceTable(prepaymentInfoDto.getSourceTable());
            debitRecord.setSourceId(prepaymentInfoDto.getSourceId());
            debitRecord.setReadyAmt(prepaymentInfoDto.getReceivableAmount());
            debitRecord.setSucceedAmt(BigDecimal.ZERO);
            debitRecord.setGoingAmt(BigDecimal.ZERO);
            debitRecord.setDebitStatus(status);
            debitRecord.setTermNo(null);
            debitRecord.setRevision(BigDecimal.ZERO.longValue());
            /**  当日核销金额  **/
            debitRecord.setWriteOffAmt(BigDecimal.ZERO);

            debitRecord.setPlanDate(prepaymentInfoDto.getRepayDate());
            debitRecord.setRecordDate(batchDate);
            debitRecord.setManualNum(BigDecimal.ZERO.intValue());

            List<BasicBankCard> bankCards = basicBankCardService.list(Wrappers.<BasicBankCard>query().lambda()
                    .eq(BasicBankCard::getContractNo,debitRecord.getContractNo())
                    .eq(BasicBankCard::getVerStatus, VerStatusEnum.alreadySign)
                    .orderByDesc(BasicBankCard::getCreateTime)
            );

            if(CollectionUtils.isEmpty(bankCards)){
                throw new AfsBaseException("该合同不存在已签约的扣款银行卡，不可使用代扣方式收款");
            }
            BasicBankCard bankCard = bankCards.get(0);
            debitRecord.setDebitSignBank(bankCard.getBankCode());
            debitRecord.setDebitSignCode(bankCard.getVerCode());
            debitRecord.setDebitEbankCode(bankCard.getEbankCode());
            debitRecord.setDebitAccount(bankCard.getAccountNo());
            debitRecord.setDebitAccountName(bankCard.getAccountName());
            debitRecord.setDebitCardPhone(bankCard.getBankPhone());
            debitRecord.setDebitBankBranch(bankCard.getBankBranch());
            debitRecord.setDebitCertNo(bankCard.getCertNo());

            this.save(debitRecord);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addDebitRecordList(Date batchDate) {

        /**  产生新的扣款数据  **/
        List<BasicDebitRecord> basicDebitRecords = new ArrayList<>();
        /**  提前还款的数据处理 by ZC.GUO  **/
        List<String> prepaymentContractNos = new ArrayList<>();
        IResponse<List<PrepaymentInfoDto>> response = custChangeFeign.getPrepaymentRecordInfo();
        if (CommonConstants.SUCCESS.equals(response.getCode())){
            List<PrepaymentInfoDto> prepaymentInfoDtoList = response.getData();
            if (EmptyUtils.isNotEmpty(prepaymentInfoDtoList)){
                prepaymentContractNos = prepaymentInfoDtoList.stream().map(PrepaymentInfoDto::getContractNo).collect(Collectors.toList());
                basicDebitRecords = this.takePrepaymentRecord(null,basicDebitRecords,prepaymentInfoDtoList,batchDate);
            }
        }
        /**  合同还款计划表basic_repayment_plan，可获取本金、利息  **/
        basicDebitRecords = this.takeRepaymentPlan(basicDebitRecords,null,prepaymentContractNos,batchDate);
        /**  合同费用记录表basic_expense_record，罚息、提前结清款、催收费用、变更费用、提前还款手续费、其他  **/
        basicDebitRecords = this.takeExpenseRecord(basicDebitRecords,null,prepaymentContractNos,batchDate);

        /**  数据归档  **/
        this.storeDebitRecord(null);

        this.saveBatch(basicDebitRecords);
    }

    /**
     *
     * <p>Description: </p>
     *
     * @param prepaymentInfoDtoList 提前还款信息集合
     * @param basicDebitRecords 目标待扣款数据集合
     * @return
     */
    private List<BasicDebitRecord> takePrepaymentRecord(String contractNo,List<BasicDebitRecord> basicDebitRecords,List<PrepaymentInfoDto> prepaymentInfoDtoList, Date batchDate){

        if (EmptyUtils.isNotEmpty(prepaymentInfoDtoList)){
            for (PrepaymentInfoDto dto:prepaymentInfoDtoList){
                /**  是否需要使用代扣收款 by ZC.GUO  **/
                if (ReceiptSourceTypeEnum.bankDeduct.equals(dto.getRepayMode())){
                    /**  当日是否为约定扣款日 by ZC.GUO  **/
                    if (DateUtil.isSameDay(batchDate,dto.getRepayDate())){
                        /**  如果指定了合同号码，则只生成该合同号码的扣款指令 by ZC.GUO  **/
                        if (EmptyUtils.isNotEmpty(contractNo)){
                            if (!contractNo.equals(dto.getContractNo())){
                                continue;
                            }
                        }
                        BasicDebitRecord debitRecord = new BasicDebitRecord();
                        debitRecord.setContractNo(dto.getContractNo());
                        debitRecord.setFeeType(FeeTypeEnum.advanceSettle);
                        debitRecord.setSourceTable(dto.getSourceTable());
                        debitRecord.setSourceId(dto.getSourceId());
                        debitRecord.setReadyAmt(dto.getReceivableAmount());
                        debitRecord.setSucceedAmt(BigDecimal.ZERO);
                        debitRecord.setGoingAmt(BigDecimal.ZERO);
                        debitRecord.setDebitStatus(DebitStatusEnum.mayDebit);
                        debitRecord.setTermNo(null);
                        debitRecord.setRevision(BigDecimal.ZERO.longValue());
                        /**  当日核销金额  **/
                        debitRecord.setWriteOffAmt(BigDecimal.ZERO);

                        debitRecord.setPlanDate(dto.getRepayDate());
                        debitRecord.setRecordDate(batchDate);
                        debitRecord.setManualNum(BigDecimal.ZERO.intValue());

                        List<BasicBankCard> bankCards = basicBankCardService.list(Wrappers.<BasicBankCard>query().lambda()
                                .eq(BasicBankCard::getContractNo,debitRecord.getContractNo())
                                .eq(BasicBankCard::getVerStatus, VerStatusEnum.alreadySign)
                                .orderByDesc(BasicBankCard::getCreateTime)
                        );

                        if(CollectionUtils.isEmpty(bankCards)){
                            continue;
                        }
                        BasicBankCard bankCard = bankCards.get(0);
                        debitRecord.setDebitSignBank(bankCard.getBankCode());
                        debitRecord.setDebitSignCode(bankCard.getVerCode());
                        debitRecord.setDebitEbankCode(bankCard.getEbankCode());
                        debitRecord.setDebitAccount(bankCard.getAccountNo());
                        debitRecord.setDebitAccountName(bankCard.getAccountName());
                        debitRecord.setDebitCardPhone(bankCard.getBankPhone());
                        debitRecord.setDebitBankBranch(bankCard.getBankBranch());
                        debitRecord.setDebitCertNo(bankCard.getCertNo());

                        basicDebitRecords.add(debitRecord);
                    }
                }
            }
        }

        return basicDebitRecords;
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void updateDebitRecordPrepare(BasicDebitRecord basicDebitRecord) {

        int count = basicDebitRecordMapper
                .updateWhenGenerateDebitBatch(basicDebitRecord.getGoingAmt(),basicDebitRecord.getDebitStatus()
                        ,basicDebitRecord.getBatchId(),basicDebitRecord.getId(),basicDebitRecord.getRevision());

        if(count == 0) {
            log.info("乐观锁生效，待扣款数据更新失败，id:[{}],version:[{}]", new Object[]{basicDebitRecord.getId(),basicDebitRecord.getRevision()});
            throw new AfsBaseException("乐观锁生效，待扣款数据更新失败");
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void updateDebitRecordFailed(BasicDebitRecord basicDebitRecord) {
        int count = basicDebitRecordMapper
                .updateWhenGenerateDebitFailed(basicDebitRecord.getGoingAmt(),basicDebitRecord.getDebitStatus()
                        ,basicDebitRecord.getBatchId(),basicDebitRecord.getManualNum(),basicDebitRecord.getId(),basicDebitRecord.getRevision());

        if(count == 0) {
            log.info("乐观锁生效，待扣款数据更新失败，id:[{}],version:[{}]", new Object[]{basicDebitRecord.getId(),basicDebitRecord.getRevision()});
            throw new AfsBaseException("乐观锁生效，待扣款数据更新失败");
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void updateDebitRecordSucceed(BasicDebitRecord basicDebitRecord) {
        int count = basicDebitRecordMapper
                .updateWhenGenerateDebitSuccess(basicDebitRecord.getSucceedAmt(),basicDebitRecord.getGoingAmt(),basicDebitRecord.getWriteOffAmt(),
                        basicDebitRecord.getDebitStatus(),basicDebitRecord.getId(),basicDebitRecord.getRevision());

        if(count == 0) {
            log.info("乐观锁生效，待扣款数据更新失败，id:[{}],version:[{}]", new Object[]{basicDebitRecord.getId(),basicDebitRecord.getRevision()});
            throw new AfsBaseException("乐观锁生效，待扣款数据更新失败");
        }
    }

    @Override
    public IPage<SingleDebitReadyVo> selectSingleDebitReadyContract(Page page, SingleDebitCondition query) {
        return baseMapper.selectSingleDebitReadyContract(page,query);
    }

    @Override
    public BasicDebitRecord selectAmountAndTimeByContractNo(String contractNo) {
        return baseMapper.selectAmountAndTimeByContractNo(contractNo);
    }

    @Override
    public List<BasicDebitRecord> handleRecordListWhenDownGrade(List<BasicDebitRecord> recordList,BasicDebitRecord record) {
        Integer manualNum = record.getManualNum();

        List<BasicDebitRecord> resultList;
        switch (manualNum){
            /**  手动扣款失败0次，第1次扣款 by ZC.GUO  **/
            case 0:
                resultList = this.handleByGrade(record,BigDecimal.ONE,new BigDecimal("10000000"));
                break;
            /**  手动扣款失败1次，第2次扣款 by ZC.GUO  **/
            case 1:
                resultList = this.handleByGrade(record,new BigDecimal("0.8"),new BigDecimal("20000"));
                break;
            /**  手动扣款失败2次，第3次扣款 by ZC.GUO  **/
            case 2:
                resultList = this.handleByGrade(record,new BigDecimal("0.6"),new BigDecimal("10000"));
                break;
            /**  手动扣款失败3次，第4次扣款 by ZC.GUO  **/
            case 3:
                resultList = this.handleByGrade(record,new BigDecimal("0.4"),new BigDecimal("5000"));
                break;
            /**  手动扣款失败4次，第5次扣款 by ZC.GUO  **/
            case 4:
                resultList = this.handleByGrade(record,new BigDecimal("0.2"),new BigDecimal("3000"));
                break;
            /**  手动扣款失败超过4次，第6次及更多次扣款 by ZC.GUO  **/
            default:
                resultList = this.handleByGrade(record,new BigDecimal("0.1"),new BigDecimal("1000"));
                break;
        }
        return resultList;
    }

    private List<BasicDebitRecord> handleByGrade(BasicDebitRecord record,BigDecimal rate,BigDecimal topAmount){

        /**  最大扣款金额限制 by ZC.GUO  **/
        BigDecimal unSuccessAmount = (record.getReadyAmt().subtract(record.getSucceedAmt())).multiply(rate).setScale(2,BigDecimal.ROUND_UP);
        if (unSuccessAmount.compareTo(topAmount) > 0){
            unSuccessAmount = topAmount;
        }
        List<BasicDebitRecord> resultList = new ArrayList<>();
        /**  罚息 by ZC.GUO  **/
        List<BasicDebitRecord> penaltyList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.penalty)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,penaltyList);
        /**  费用 by ZC.GUO  **/
        List<BasicDebitRecord> urgeList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.urge)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,urgeList);

        List<BasicDebitRecord> changeCostList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.changeCost)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,changeCostList);

        List<BasicDebitRecord> otherList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.other)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,otherList);
        /**  90天以下租金，时间久的优先，先利后本 by ZC.GUO  **/
        List<FeeTypeEnum> rent = new ArrayList<>();
        rent.add(FeeTypeEnum.capital);
        rent.add(FeeTypeEnum.interest);

        List<BasicDebitRecord> rentLessThan90List = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .in(BasicDebitRecord::getFeeType,rent)
                .le(BasicDebitRecord::getOverdueDays,90)
                .orderByAsc(BasicDebitRecord::getTermNo)
                .orderByDesc(BasicDebitRecord::getFeeType)
                .orderByDesc(BasicDebitRecord::getOverdueDays)
                .orderByAsc(BasicDebitRecord::getOverdueDays)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,rentLessThan90List);
        /**  90天以上本金，时间久的优先 by ZC.GUO  **/
        List<BasicDebitRecord> capitalMoreThan90List = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.capital)
                .gt(BasicDebitRecord::getOverdueDays,90)
                .orderByDesc(BasicDebitRecord::getOverdueDays)
                .orderByAsc(BasicDebitRecord::getOverdueDays)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,capitalMoreThan90List);

        /**  90天以上利息，时间久的优先 by ZC.GUO  **/
        List<BasicDebitRecord> interestMoreThan90List = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.interest)
                .gt(BasicDebitRecord::getOverdueDays,90)
                .orderByDesc(BasicDebitRecord::getOverdueDays)
                .orderByAsc(BasicDebitRecord::getOverdueDays)
        );
        unSuccessAmount = this.handleRecordList(unSuccessAmount,resultList,interestMoreThan90List);

        /**  其他扣款项，比如提前结清款 by ZC.GUO  **/

        /**  最大扣款金额限制重新计算（提前结清扣款不降档、无最高扣款额限制） by ZC.GUO  **/
        unSuccessAmount = (record.getReadyAmt().subtract(record.getSucceedAmt())).multiply(BigDecimal.ONE).setScale(2,BigDecimal.ROUND_UP);
        List<BasicDebitRecord> advanceSettleList = this.list(Wrappers.<BasicDebitRecord>query().lambda()
                .eq(BasicDebitRecord::getContractNo,record.getContractNo())
                .eq(BasicDebitRecord::getDebitStatus,DebitStatusEnum.mayDebit)
                .eq(BasicDebitRecord::getFeeType,FeeTypeEnum.advanceSettle)
        );
        this.handleRecordList(unSuccessAmount,resultList,advanceSettleList);

        return resultList;
    }

    private BigDecimal handleRecordList(BigDecimal unSuccessAmount,List<BasicDebitRecord> resultList,List<BasicDebitRecord> sourceList){

        if (CollectionUtils.isNotEmpty(sourceList)){
            for (BasicDebitRecord debitRecord : sourceList){
                if (unSuccessAmount.compareTo(BigDecimal.ZERO) > 0){
                    /**  未达到最大扣款金额，本记录扣款 by ZC.GUO  **/
                    BigDecimal amount = debitRecord.getReadyAmt().subtract(debitRecord.getSucceedAmt());
                    /**  可全额扣款 by ZC.GUO  **/
                    if(unSuccessAmount.compareTo(amount) > 0){
                        debitRecord.setGoingAmt(amount);
                        unSuccessAmount = unSuccessAmount.subtract(amount);
                    }
                    /**  不可全额扣款 by ZC.GUO  **/
                    else{
                        debitRecord.setGoingAmt(unSuccessAmount);
                        unSuccessAmount = BigDecimal.ZERO;
                    }
                }else{
                    /**  已经达到最大扣款金额，本记录不扣款且不能再发起其他扣款 by ZC.GUO  **/
                    debitRecord.setGoingAmt(BigDecimal.ZERO);
                }
                debitRecord.setDebitStatus(DebitStatusEnum.debiting);
                resultList.add(debitRecord);
            }
        }
        return unSuccessAmount;
    }

    /**
     *
     * <p>Description: 每日代扣数据归档</p>
     *
     * @param contractNo 合同编号
     * @return
     */
    private void storeDebitRecord(String contractNo){
        List<BasicDebitRecordHistory> debitRecordHistoryList = new ArrayList<>();
        List<BasicDebitRecord> basicDebitRecords = this.list(Wrappers.<BasicDebitRecord>query().lambda()
            .eq(StringUtils.isNotBlank(contractNo),BasicDebitRecord::getContractNo,contractNo)
        );
        if(CollectionUtils.isNotEmpty(basicDebitRecords)){
            basicDebitRecords.forEach(basicDebitRecord -> {
                if (basicDebitRecord.getGoingAmt().compareTo(BigDecimal.ZERO)>0){
                    log.warn("尚有扣款中款项未回盘，需要处理完成后重新生成待扣款数据，待扣款数据id为：{}，扣款批次id为：{}",basicDebitRecord.getId(),basicDebitRecord.getBatchId());
                    throw new AfsBaseException("尚有扣款中款项未回盘，需要处理完成后重新生成待扣款数据，待扣款数据生成失败");
                }
                BasicDebitRecordHistory debitRecordHistory = new BasicDebitRecordHistory();
                BeanUtils.copyProperties(basicDebitRecord,debitRecordHistory);
                debitRecordHistory.setId(null);
                debitRecordHistory.setStoreDate(Calendar.getInstance().getTime());
                debitRecordHistoryList.add(debitRecordHistory);
                /**  删除本条数据  **/
                this.removeById(basicDebitRecord.getId());
            });
        }
        /**  历史数据保存  **/
        if(CollectionUtils.isNotEmpty(debitRecordHistoryList)){
            basicDebitRecordHistoryService.saveBatch(debitRecordHistoryList);
        }
    }

    /**
     *
     * <p>Description: </p>
     *
     * @param contractNo 合同编号
     * @param basicDebitRecords 目标待扣款数据集合
     * @param exceptContractNos 不进行扣款的合同号
     * @return
     */
    private List<BasicDebitRecord> takeExpenseRecord(List<BasicDebitRecord> basicDebitRecords,String contractNo,List<String> exceptContractNos, Date batchDate){

        List<BasicExpenseRecord> basicExpenseRecordList = basicExpenseRecordService.list(Wrappers.<BasicExpenseRecord>query().lambda()
                .eq(BasicExpenseRecord::getStatus, ExpenseStatusEnum.unsettle)
                .eq(StringUtils.isNotBlank(contractNo),BasicExpenseRecord::getContractNo,contractNo)
                /**  已有生效提前结清的不进行常规扣款 by ZC.GUO  **/
                .notIn(EmptyUtils.isNotEmpty(exceptContractNos),BasicExpenseRecord::getContractNo,exceptContractNos)
                .orderByAsc(BasicExpenseRecord::getId));
        if(CollectionUtils.isNotEmpty(basicExpenseRecordList)){
            for (BasicExpenseRecord basicExpenseRecord : basicExpenseRecordList ){
                BasicDebitRecord debitRecord = new BasicDebitRecord();
                debitRecord.setContractNo(basicExpenseRecord.getContractNo());
                debitRecord.setFeeType(basicExpenseRecord.getCostType());
                debitRecord.setSourceTable(BasicExpenseRecord.class.getAnnotation(TableName.class).value());
                debitRecord.setSourceId(basicExpenseRecord.getId());
                debitRecord.setReadyAmt(basicExpenseRecord.getRemainingAmt());
                debitRecord.setSucceedAmt(BigDecimal.ZERO);
                debitRecord.setGoingAmt(BigDecimal.ZERO);
                debitRecord.setDebitStatus(DebitStatusEnum.mayDebit);
                debitRecord.setTermNo(null);
                debitRecord.setRevision(BigDecimal.ZERO.longValue());
                /**  当日核销金额  **/
                debitRecord.setWriteOffAmt(BigDecimal.ZERO);

                debitRecord.setPlanDate(null);
                debitRecord.setRecordDate(batchDate);
                debitRecord.setManualNum(BigDecimal.ZERO.intValue());

                List<BasicBankCard> bankCards = basicBankCardService.list(Wrappers.<BasicBankCard>query().lambda()
                        .eq(BasicBankCard::getContractNo,debitRecord.getContractNo())
                        .eq(BasicBankCard::getVerStatus, VerStatusEnum.alreadySign)
                        .orderByDesc(BasicBankCard::getCreateTime)
                );

                if(CollectionUtils.isEmpty(bankCards)){
                    continue;
                }
                BasicBankCard bankCard = bankCards.get(0);
                debitRecord.setDebitSignBank(bankCard.getBankCode());
                debitRecord.setDebitSignCode(bankCard.getVerCode());
                debitRecord.setDebitEbankCode(bankCard.getEbankCode());
                debitRecord.setDebitAccount(bankCard.getAccountNo());
                debitRecord.setDebitAccountName(bankCard.getAccountName());
                debitRecord.setDebitCardPhone(bankCard.getBankPhone());
                debitRecord.setDebitBankBranch(bankCard.getBankBranch());
                debitRecord.setDebitCertNo(bankCard.getCertNo());

                basicDebitRecords.add(debitRecord);
            }
        }

        return basicDebitRecords;
    }

    /**
     *
     * <p>Description: </p>
     *
     * @param contractNo 合同编号
     * @param basicDebitRecords 目标待扣款数据集合
     * @param exceptContractNos 不进行扣款的合同号
     * @return
     */
    private List<BasicDebitRecord> takeRepaymentPlan(List<BasicDebitRecord> basicDebitRecords, String contractNo, List<String> exceptContractNos, Date batchDate){

        List<RepaymentStatusEnum> repaymentStatusEnumList = new ArrayList<RepaymentStatusEnum>();
        repaymentStatusEnumList.add(RepaymentStatusEnum.UNPAID);
        repaymentStatusEnumList.add(RepaymentStatusEnum.OVERDUE);
        List<BasicRepaymentPlan> basicRepaymentPlanList = basicRepaymentPlanService.list(Wrappers.<BasicRepaymentPlan>query().lambda()
                .in(BasicRepaymentPlan::getStatus,repaymentStatusEnumList)
                .eq(StringUtils.isNotBlank(contractNo),BasicRepaymentPlan::getContractNo,contractNo)
                /**  已有生效提前结清的不进行常规扣款 by ZC.GUO  **/
                .notIn(EmptyUtils.isNotEmpty(exceptContractNos),BasicRepaymentPlan::getContractNo,exceptContractNos)
                .le(BasicRepaymentPlan::getDueDate,DateUtil.endOfDay(batchDate))
                .orderByAsc(BasicRepaymentPlan::getId)
        );
        if(CollectionUtils.isNotEmpty(basicRepaymentPlanList)){
            for (BasicRepaymentPlan basicRepaymentPlan : basicRepaymentPlanList){
                /**  本金  **/
                BasicDebitRecord capitalDebitRecord = new BasicDebitRecord();
                capitalDebitRecord.setContractNo(basicRepaymentPlan.getContractNo());
                capitalDebitRecord.setFeeType(FeeTypeEnum.capital);
                capitalDebitRecord.setSourceTable(BasicRepaymentPlan.class.getAnnotation(TableName.class).value());
                capitalDebitRecord.setSourceId(basicRepaymentPlan.getId());
                capitalDebitRecord.setReadyAmt(basicRepaymentPlan.getReceivablePrinciple().subtract(ObjectUtils.isNotEmpty(basicRepaymentPlan.getActualPrinciple())?basicRepaymentPlan.getActualPrinciple():BigDecimal.ZERO));
                capitalDebitRecord.setSucceedAmt(BigDecimal.ZERO);
                capitalDebitRecord.setGoingAmt(BigDecimal.ZERO);
                capitalDebitRecord.setDebitStatus(DebitStatusEnum.mayDebit);
                capitalDebitRecord.setTermNo(basicRepaymentPlan.getTermNo());
                capitalDebitRecord.setRevision(BigDecimal.ZERO.longValue());
                /**  当日核销金额  **/
                capitalDebitRecord.setWriteOffAmt(BigDecimal.ZERO);

                capitalDebitRecord.setPlanDate(basicRepaymentPlan.getDueDate());
                capitalDebitRecord.setRecordDate(batchDate);
                capitalDebitRecord.setManualNum(BigDecimal.ZERO.intValue());

                capitalDebitRecord.setOverdueDays(BigDecimal.valueOf(DateUtil.between(capitalDebitRecord.getPlanDate(),batchDate, DateUnit.DAY)).intValue());


                List<BasicBankCard> bankCards = basicBankCardService.list(Wrappers.<BasicBankCard>query().lambda()
                        .eq(BasicBankCard::getContractNo,capitalDebitRecord.getContractNo())
                        .eq(BasicBankCard::getVerStatus, VerStatusEnum.alreadySign)
                        .orderByDesc(BasicBankCard::getCreateTime)
                );

                if(CollectionUtils.isEmpty(bankCards)){
                    continue;
                }
                BasicBankCard bankCard = bankCards.get(0);
                capitalDebitRecord.setDebitSignBank(bankCard.getBankCode());
                capitalDebitRecord.setDebitSignCode(bankCard.getVerCode());
                capitalDebitRecord.setDebitEbankCode(bankCard.getEbankCode());
                capitalDebitRecord.setDebitAccount(bankCard.getAccountNo());
                capitalDebitRecord.setDebitAccountName(bankCard.getAccountName());
                capitalDebitRecord.setDebitCardPhone(bankCard.getBankPhone());
                capitalDebitRecord.setDebitBankBranch(bankCard.getBankBranch());
                capitalDebitRecord.setDebitCertNo(bankCard.getCertNo());

                /**  利息  **/
                BasicDebitRecord interestDebitRecord = new BasicDebitRecord();
                BeanUtils.copyProperties(capitalDebitRecord,interestDebitRecord);
                interestDebitRecord.setFeeType(FeeTypeEnum.interest);
                interestDebitRecord.setReadyAmt(basicRepaymentPlan.getReceivableInterest().subtract(ObjectUtils.isNotEmpty(basicRepaymentPlan.getActualInterest())?basicRepaymentPlan.getActualInterest():BigDecimal.ZERO));

                if (capitalDebitRecord.getReadyAmt().compareTo(BigDecimal.ZERO)>0){
                    basicDebitRecords.add(capitalDebitRecord);
                }
                if (interestDebitRecord.getReadyAmt().compareTo(BigDecimal.ZERO)>0){
                    basicDebitRecords.add(interestDebitRecord);
                }

            }
        }
        return basicDebitRecords;
    }
}
